home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Plotting / aa_Intel_Only / Gnuplot / GnuplotSource / Gnuplot.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  15.8 KB  |  737 lines

  1. /*
  2.  *  Copyright (C) 1993  Robert Davis
  3.  *
  4.  *  This program is free software; you can redistribute it and/or
  5.  *  modify it under the terms of Version 2, or any later version, of 
  6.  *  the GNU General Public License as published by the Free Software 
  7.  *  Foundation.
  8.  */
  9.  
  10. static char RCSId[]="$Id: Gnuplot.m,v 1.12 1993/05/30 09:10:13 davis Exp $";
  11.  
  12.  
  13. #import <appkit/Application.h>
  14. #import <appkit/Listener.h>
  15. #import <appkit/Matrix.h>
  16. #import <appkit/Menu.h>
  17. #import <appkit/MenuCell.h>
  18. #import <appkit/OpenPanel.h>
  19. #import <appkit/SavePanel.h>
  20. #import <appkit/PrintInfo.h>
  21. #import <appkit/Speaker.h>
  22.  
  23. #import <appkit/publicWraps.h>        /* NXConvertWinNumToGlobal()    */
  24.  
  25. #import <objc/List.h>
  26. #import <objc/NXStringTable.h>
  27.  
  28. #import <libc.h>            /* MAXPATHLEN            */
  29. #import <math.h>            /* M_PI                */
  30. #import <strings.h>            /* strcmp()            */
  31.  
  32. #import "Gnuplot.h"
  33. #import "GnuplotPlot.h"
  34. #import "Inspector.h"
  35. #import "Preferences.h"
  36. #import "Version.h"
  37.  
  38. #define MAX_ID_LEN 50
  39.  
  40. extern struct value {            /* structs from plot.h        */
  41.  
  42.     enum DATA_TYPES {INTGR, CMPLX} type;
  43.     union {
  44.     int        int_val;
  45.     struct cmplx {
  46.         double real, imag;
  47.     } cmplx_val;
  48.     } v;
  49.  
  50. } *Gcomplex();
  51.  
  52. extern struct udvt_entry {
  53.  
  54.     struct udvt_entry    *next_udv;
  55.     char        udv_name[MAX_ID_LEN+1];
  56.     int            udv_undef;
  57.     struct value    udv_value;
  58.  
  59. } *first_udv;
  60.  
  61.  
  62.  
  63. /*
  64.  *  This function is from the NeXTSTEP Developer Example Draw by Paul 
  65.  *  Hegarty.
  66.  *  
  67.  *  Sets the updateAction for every menu item that sends its action to 
  68.  *  the First Responder (i.e. every menu item whose target is nil).  
  69.  *  When autoupdate is on (which is always in this app), each event 
  70.  *  will be followed by an update of every visible menu item.  This 
  71.  *  keeps all unavailable menu items dimmed and disabled so that the 
  72.  *  user knows what options are available at any given time.
  73.  */ 
  74. static void initMenu (Menu* menu)
  75. {
  76.     int count;
  77.     Matrix *matrix;
  78.     MenuCell *cell;
  79.     id matrixTarget, cellTarget;
  80.  
  81.     matrix = [menu itemList];
  82.     matrixTarget = [matrix target];
  83.  
  84.     count = [matrix cellCount];
  85.     while (count--) {
  86.  
  87.         cell = [matrix cellAt:count :0];
  88.         cellTarget = [cell target];
  89.  
  90.         if (!matrixTarget && !cellTarget)
  91.             [cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
  92.         else if ([cell hasSubmenu])
  93.             initMenu(cellTarget);
  94.  
  95.     }
  96. }
  97.  
  98.  
  99. @implementation Gnuplot
  100.  
  101. + setConstantUpdate:(int)updateType
  102. {
  103.     return [GnuplotPlot setConstantUpdate:updateType];
  104. }
  105.  
  106.  
  107. + setHalvePlot:(BOOL)condition
  108. {
  109.     return [GnuplotPlot setHalvePlot:condition];
  110. }
  111.  
  112.  
  113. - init
  114. {
  115.     [super init];
  116.  
  117.     zone = [self zone];
  118.  
  119.     docList = [[List allocFromZone: zone] init];
  120.     numUntitled = 1;
  121.     isPoweringOff = NO;
  122.  
  123.     /* Set printinfo margins to 1/2 inch (36 points) on each side */
  124.     [[NXApp printInfo] setMarginLeft:36.0 right:36.0 top:36.0 bottom:36.0];
  125.  
  126.     [NXApp setAutoupdate:YES];
  127.  
  128.     preferences = [[Preferences allocFromZone: zone] init];
  129.     inspector = [[Inspector allocFromZone: zone] init];
  130.  
  131.     /*  Setup the first user-defined variable to be pi  */
  132.     Gcomplex(&(first_udv->udv_value), M_PI, 0.0);
  133.  
  134.     return self;
  135. }
  136.  
  137.  
  138. // This is never called
  139. - free
  140. {
  141.     [infoPanel free];
  142.     [copyingPanel free];
  143.     [inspector free];
  144.     [preferences free];
  145.     [stringSet free];
  146.     [docList freeObjects];
  147.     [docList free];
  148.  
  149.     return [super free];
  150. }
  151.  
  152.  
  153. - stringSet
  154. {
  155.     return stringSet;
  156. }
  157.  
  158.  
  159. - showInfoPanel:sender
  160. {
  161.     if (!infoPanel) {
  162.     extern char    version[];
  163.     char        rev[255];
  164.  
  165.     [NXApp loadNibSection: "InfoPanel.nib" 
  166.                         owner: self
  167.                     withNames: NO
  168.                  fromZone: zone];
  169.  
  170.     /*  
  171.      *  nextstep_version is a string constant defined and 
  172.      *  initialized in Version.h that is automatically update by 
  173.      *  RCS.  It will contain "$Revision: " followed by the 
  174.      *  revision number.  The last character will be a '$'.  
  175.      *  version is a string defined in version.c and is only a 
  176.      *  number.
  177.      */
  178.  
  179.     strcpy (rev, nextstep_version);
  180.     rev[strlen(rev) - 1] = '\0';            /* Remove last $ */
  181.     [nextstepVersionTextField setStringValue:&(rev[11])];
  182.     [versionTextField setStringValue:version];
  183.     }
  184.  
  185.     [infoPanel makeKeyAndOrderFront:self];
  186.     return self;
  187. }
  188.  
  189.  
  190.  
  191. - showCopyingPanel:sender
  192. {
  193.     if (!copyingPanel)
  194.     [NXApp loadNibSection: "CopyingPanel.nib"
  195.                         owner: self
  196.                     withNames: NO
  197.                      fromZone: zone];
  198.  
  199.     [copyingPanel makeKeyAndOrderFront:self];
  200.  
  201.     return self;
  202. }
  203.  
  204.  
  205.  
  206. - showInspectorPane:sender
  207. {
  208.     /* Inspector was created in -init */
  209.  
  210.     [inspector selectPane:[sender selectedTag]];
  211.     [[inspector window] makeKeyAndOrderFront:self];
  212.     return self;
  213. }
  214.  
  215.  
  216.  
  217. - showPreferencesPanel:sender
  218. {
  219.     /* Preferences was created in -init */
  220.  
  221.     [preferences showPanel:self];
  222.     return self;
  223. }
  224.  
  225.  
  226.  
  227. - new:sender
  228. {
  229.     id newDoc = nil;
  230.  
  231.     if (newDoc = [[GnuplotPlot allocFromZone: zone] init])  {
  232.     numUntitled++;
  233.     [docList addObject:newDoc];
  234.     }
  235.  
  236.     return newDoc;
  237. }
  238.  
  239.  
  240.  
  241. - open:sender
  242. {
  243.     const char *const *files;
  244.     static const char *const fileType[2] = {DOCUMENT_TYPE, NULL};
  245.     OpenPanel *openPanel;
  246.     id newDoc;
  247.     char fullName[MAXPATHLEN];
  248.  
  249.     openPanel = [[OpenPanel new] allowMultipleFiles:YES];
  250.     [openPanel setTitle:[stringSet valueForStringKey:"openT"]];
  251.  
  252.     /* Get a list of files to open from the user. */
  253.     if ([openPanel runModalForTypes:fileType]) {
  254.  
  255.     /* For each pathname selected... */
  256.     for (files = [openPanel filenames]; files && *files; files++) {
  257.  
  258.         sprintf (fullName, "%s/%s", [openPanel directory], *files);
  259.  
  260.         /* 
  261.          *  ... check to see if the doc is already opened.  If it 
  262.          *  is, bring it to the front.
  263.          */
  264.         if (newDoc = [self isDocOpen: fullName])
  265.         [[newDoc window] makeKeyAndOrderFront:self];
  266.  
  267.         /* If not, try to create a new one. */
  268.         else if ((newDoc = [[GnuplotPlot allocFromZone: zone]
  269.                           initFromFile: fullName]))
  270.         [docList addObject: newDoc];
  271.  
  272.  
  273.         /* 
  274.          *  Otherwise, just return.  GnuplotPlot will report the 
  275.          *  nature of the error to the user so we don't need to 
  276.          *  here.
  277.          */
  278.         else
  279.         return nil;
  280.  
  281.     }
  282.  
  283.     }
  284.  
  285.     return self;
  286. }
  287.  
  288.  
  289.  
  290. - saveAll:sender
  291. {
  292.     [docList makeObjectsPerform:@selector(save:) with:self];
  293.     return self;
  294. }
  295.  
  296.  
  297. - setSaveType:sender
  298. {
  299.     id    savePanel = [SavePanel new];
  300.  
  301.     switch ([sender selectedTag]) {
  302.     case SAVE_GNUPLOT:
  303.     [savePanel setRequiredFileType:DOCUMENT_TYPE]; break;
  304.     case SAVE_EPS:
  305.     [savePanel setRequiredFileType:"eps"]; break;
  306.     case SAVE_TIFF:
  307.     [savePanel setRequiredFileType:"tiff"]; break;
  308.     }
  309.  
  310.     return self;
  311. }
  312.  
  313.  
  314. - (int)saveType
  315. {
  316.     return [savePanelTypeMatrix selectedTag];
  317. }
  318.  
  319.  
  320. /* 
  321.  *  This method should update any panels external to the doc which 
  322.  *  reflect the current doc (e.g. the Inspector panel).
  323.  */
  324. - updateApp
  325. {
  326.     [inspector update];
  327.     return self;
  328. }
  329.  
  330.  
  331.  
  332.  
  333. /*  
  334.  *  The doc should send this when it closes so that we can remove it 
  335.  *  from our doc list.
  336.  */
  337. - docDidClose:sender
  338. {
  339.     [docList removeObject:sender];
  340.     [self setCurrentDoc:nil];
  341.     return self;
  342. }
  343.  
  344.  
  345.  
  346. /*  
  347.  *  Checks the doc list to see if the doc specified by fullPath is 
  348.  *  currently open.  If it is, the doc is returned.  Otherwise, nil is 
  349.  *  returned.
  350.  */
  351. - isDocOpen: (const char *) fullPath
  352. {
  353.     int counter, docCount = [docList count];
  354.     id aDoc;
  355.  
  356.     for (counter = 0 ; counter < docCount ; counter++)
  357.     if (!strcmp ([aDoc = [docList objectAt: counter] name], fullPath))
  358.         return aDoc;
  359.  
  360.     return nil;
  361. }
  362.  
  363.  
  364.  
  365.  
  366. - (int)numberNew
  367. {
  368.     return numUntitled;
  369. }
  370.  
  371.  
  372.  
  373. - setCurrentDoc:(GnuplotPlot *)aDoc
  374. {
  375.     if (aDoc)
  376.     currentDoc = aDoc;
  377.     else
  378.     currentDoc = [[NXApp mainWindow] delegate];
  379.  
  380.     return self;
  381. }
  382.  
  383.  
  384. - (GnuplotPlot *)currentDoc
  385. {
  386.     return currentDoc;
  387. }
  388.  
  389.  
  390.  
  391.  
  392. /*** Auto-Update Methods ***/
  393.  
  394.  
  395.  
  396. /*
  397.  *  This method is from the NeXTSTEP example Draw by Paul Hegarty.
  398.  *
  399.  *  When a menu is updated, this method is sent by each menu item that 
  400.  *  sends its action to the First Responder.  If the object that would 
  401.  *  respond to an action sent down the Responder chain also responds 
  402.  *  to the validateCommand:, then we send validateCommand: to 
  403.  *  determine whether the menu action is valid now.  Otherwise, if 
  404.  *  there is a Responder to the message, then we assume that the item 
  405.  *  is valid.  The method returns YES if the cell has changed its 
  406.  *  appearance (so that the caller Menu knows to redraw it).
  407.  */
  408. - (BOOL)menuItemUpdate:menuCell
  409. {
  410.     SEL action;
  411.     id responder, target;
  412.     BOOL enable;
  413.  
  414.     target = [menuCell target];
  415.     enable = [menuCell isEnabled];
  416.  
  417.     if (!target) {
  418.     action = [menuCell action];
  419.     responder = [NXApp calcTargetForAction:action];
  420.     if ([responder respondsTo:@selector(validateCommand:)])
  421.         enable = [responder validateCommand:menuCell];
  422.     else
  423.         enable = responder ? YES : NO;
  424.  
  425.     }
  426.  
  427.     if ([menuCell isEnabled] != enable) {
  428.     [menuCell setEnabled:enable];
  429.     return YES;
  430.     } else
  431.         return NO;
  432.  
  433. }
  434.  
  435.  
  436. /*  
  437.  *  This method is sent down the Responder chain by menuItemUpdate:
  438.  *  
  439.  *  Every object that responds to messages sent down the Responder 
  440.  *  chain by menu cells but occasionally can't do perform the action 
  441.  *  should implement validateCommand: and return a boolean value 
  442.  *  telling whether the menu item should be enabled or not.
  443.  *  
  444.  *  For example:  we can respond to saveAll:, but we can't save all if 
  445.  *  there are no docs that need saving.  So we need to implement 
  446.  *  validateCommand: to  disable the saveAll: menu cell when it is 
  447.  *  irrelevant.
  448.  */
  449. - (BOOL)validateCommand:menuCell
  450. {
  451.     SEL action = [menuCell action];
  452.     int count;
  453.  
  454.     if (action == @selector(saveAll:))  {
  455.  
  456.     if ([docList count])
  457.         for (count = [docList count] - 1; count >= 0 ; count--)
  458.         if ([[docList objectAt:count] validateCommand:menuCell])
  459.             return YES;
  460.         
  461.     return NO;
  462.  
  463.     }
  464.  
  465.     return YES;
  466. }
  467.  
  468.  
  469.  
  470.  
  471.  
  472.  
  473.  
  474. /*** Application Delegate Methods ***/
  475.  
  476.  
  477. /* Sent when user opens one of our docs from the Workspace. */
  478.  
  479. - (int)app:sender openFile:(const char *)path type:(const char *)type
  480. {
  481.     id newObject;
  482.  
  483.     /* If the document is of the right type... */
  484.     if (type && !strcmp (type, DOCUMENT_TYPE)) {
  485.  
  486.     /*  
  487.      *  ... first check to see if it's already open.  If it is, 
  488.      *  bring it to the front.
  489.      */
  490.     if (newObject = [self isDocOpen: path])
  491.         [[newObject window] makeKeyAndOrderFront:self];
  492.  
  493.  
  494.     /* Otherwise, attempt to open the document. */
  495.     else if (newObject = [[GnuplotPlot allocFromZone: zone]
  496.                                 initFromFile:path]) {
  497.  
  498.         [docList addObject:newObject];
  499.  
  500.         /*  
  501.          *  Set the OpenPanel directory so that when the user opens 
  502.          *  another doc, the open panel will start where he/she left
  503.          *  off.
  504.          */
  505.         [[OpenPanel new] setDirectory:path];
  506.  
  507.         /* 
  508.          *  This causes documents opened from the Workspace to 
  509.          *  open more slowly, but if we don't the Inspector will 
  510.          *  be outdated until the next event.
  511.          */
  512.         [self updateApp];
  513.         return YES;
  514.  
  515.     }
  516.  
  517.     }
  518.  
  519.     return NO;
  520. }
  521.  
  522.  
  523. - app:sender powerOffIn:(int)ms andSave:(int)aFlag
  524. {
  525.     isPoweringOff = YES;
  526.     return [self appWillTerminate:self];
  527. }
  528.  
  529.  
  530.  
  531. - (BOOL)appAcceptsAnotherFile:sender
  532. {
  533.     /*  
  534.      *  We return YES to this because we can open any number of 
  535.      *  documents.
  536.      */
  537.     return YES;
  538. }
  539.  
  540.  
  541. - appDidInit:sender
  542. {
  543.     id    savePanel = [SavePanel new];
  544.     int    inspectorPref;
  545.  
  546.     initMenu([NXApp mainMenu]);        /* Setup auto-menu-update    */
  547.  
  548.     /*  
  549.      *  Set services delegate to be self so this app can be a 
  550.      *  service-provider and this object can handle the messages.
  551.      */
  552.     [[NXApp appListener] setServicesDelegate:self];
  553.  
  554.  
  555.     /* Set save panel's accessory view */
  556.     [savePanel setAccessoryView: [savePanelAccessory contentView]];
  557.     [self setSaveType:savePanelTypeMatrix];
  558.  
  559.     /* 
  560.      *  Open a new, untitled document, unless the app was started when 
  561.      *  the user opened a document from the Workspace or the user has 
  562.      *  specified not to with Preferences.
  563.      */
  564.     if (![docList count] && [preferences newDocument])
  565.     [self setCurrentDoc:[self new:self]];
  566.  
  567.     /* 
  568.      *  We make the inspector the key window down here, last, so that 
  569.      *  it will be key when the user begins using the program.  Select 
  570.      *  user's preferred pane, too.
  571.      */
  572.     inspectorPref = [preferences defaultInspector];
  573.     if (inspectorPref != NO_INSPECTOR) {
  574.     [inspector selectPane: inspectorPref];
  575.     [[inspector window] makeKeyAndOrderFront:self];
  576.     }
  577.  
  578.     return self;
  579. }
  580.  
  581.  
  582.  
  583. - appWillTerminate: sender
  584. {
  585.     int editedCount, counter;
  586.     id editedDocs = [[List allocFromZone: zone] init];
  587.     id aDoc;
  588.  
  589.     do {
  590.  
  591.     /*  Build a list of edited, unsaved docs. */
  592.  
  593.     [editedDocs empty];
  594.     counter = [docList count];
  595.     while (--counter >= 0) {
  596.         aDoc = [docList objectAt: counter];
  597.         if ([aDoc isDocEdited])
  598.         [editedDocs addObject: aDoc];
  599.     }
  600.  
  601.     /*  
  602.      *  If there are edited, unsaved docs, ask if the user wants to 
  603.      *  review them, quit, or cancel the quit action (unless this 
  604.      *  method was sent by us during a poweroff/logoff, in which 
  605.      *  case we don't allow a cancel).
  606.      */
  607.     if ((editedCount = [editedDocs count]) > 0)  {
  608.         switch (NXRunAlertPanel ([stringSet valueForStringKey: "quitT"],
  609.                   [stringSet valueForStringKey: "unsavedDocs"],
  610.                   [stringSet valueForStringKey: "reviewB"],
  611.                   [stringSet valueForStringKey: "quitB"],
  612.           isPoweringOff? NULL: [stringSet valueForStringKey: "cancelB"])) {
  613.  
  614.         case NX_ALERTDEFAULT:        /* Review Unsaved    */
  615.  
  616.         counter = editedCount;
  617.         while (--counter >= 0)
  618.             [[editedDocs objectAt: counter] close:self];
  619.         break;
  620.  
  621.         case NX_ALERTALTERNATE:        /* Quit Anyway        */
  622.         editedCount = 0;
  623.         break;
  624.  
  625.         case NX_ALERTOTHER:            /* Cancel        */
  626.         [editedDocs free];
  627.         return nil;
  628.         break;
  629.         }
  630.     }
  631.  
  632.     /*  
  633.      *  If the user chooses to review unsaved, then is shown a Save 
  634.      *  panel for a doc and chooses "Cancel," we end up down here, 
  635.      *  where we'll repeat if there are more unedited docs.  
  636.      *  Basically, we force the user either to save all the docs or 
  637.      *  make a decision in the quit panel to review, quit, or 
  638.      *  cancel.
  639.      */
  640.     
  641.     } while (editedCount > 0);
  642.  
  643.     [editedDocs free];
  644.  
  645.  
  646.     /*
  647.      *  Quit the application.  (We free things here because -free is 
  648.      *  never called -- NXApp simply exit()'s -- but GnuplotPlot's 
  649.      *  implementation of -free deletes the plot's tmp file, so we 
  650.      *  must call it to avoid leaving junk in /tmp.) 
  651.      */
  652.     
  653.     [docList makeObjectsPerform: @selector(free)];
  654.     [docList free];
  655.  
  656.     return self;
  657. }
  658.  
  659.  
  660.  
  661. /*  
  662.  *  This method is sent when a user chooses one of Gnuplot's services 
  663.  *  from the Services menu in another application.
  664.  */
  665. - plotData:pb userData:(const char *)userData error:(char **)errorMessage
  666. {
  667.     char    *data;
  668.     int        length, i;
  669.     const char    *const *types;
  670.     BOOL    hasFilename;
  671.  
  672.     types = [pb types];
  673.     hasFilename = NO;
  674.     for (i=0 ; !hasFilename && types[i] ; i++)
  675.     if (!strcmp (types[i], NXFilenamePboardType)) hasFilename = YES;
  676.  
  677.     if (hasFilename) {
  678.         [pb readType:NXFilenamePboardType data:&data length:&length];
  679.  
  680.     if (data && length) {
  681.         BOOL    useCurrent = (currentDoc && strcmp (userData, "new"));
  682.         char    *path = NXCopyStringBufferFromZone (data, zone);
  683.         char    *beg, *end;
  684.         BOOL    atEnd;
  685.  
  686.         /*  
  687.          *  If there is a current document, we add each data file 
  688.          *  specified on the pasteboard to it.  If there's no
  689.          *  current document, or if the userData contains "new" 
  690.          *  we create a new document and add the data file to 
  691.          *  that.
  692.          */
  693.  
  694.         beg = path;
  695.         atEnd = beg? NO:YES;
  696.         while (!atEnd) {
  697.         end = index (beg, '\t');
  698.  
  699.         if (end && *end && *end == '\t')
  700.             *end = '\0';
  701.         else
  702.             atEnd = YES;
  703.  
  704.         if (useCurrent)
  705.             [currentDoc addDataFile:beg];
  706.         else {
  707.             id    newDoc = [self new:self];
  708.             [newDoc addDataFile:beg];
  709.             [self setCurrentDoc:newDoc];
  710.             useCurrent = YES;    /* Just create one new doc for all */
  711.         }
  712.  
  713.         if (!atEnd) {
  714.             *end = '\t';
  715.             beg = end + 1;
  716.         }
  717.         }
  718.  
  719.         NXZoneFree(zone, path);
  720.         [self updateApp];
  721.     }
  722.     }
  723.  
  724.     return self;
  725. }
  726.  
  727.  
  728. // Shuts up the compiler about unused RCSId
  729. - (const char *) rcsid
  730. {
  731.     return RCSId;
  732. }
  733.  
  734.  
  735. @end
  736.  
  737.